﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Text;
using Common;
using Controladores;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Wordprocessing;
using Entidades;
using Entidades.Models;
using Entidades.Models.Sistema;
using log4net;
using log4net.Repository.Hierarchy;
using Newtonsoft.Json;
using Web.Handlers.Data;
using Web.Helpers;
using Web.Helpers.Documents.Spreadsheets.Enums;
using Web.Helpers.jqGridHelpers;
using Web.Helpers.Documents.Spreadsheets;

namespace Web.Pages
{
    public class BasePage : System.Web.UI.Page
    {
        #region Private Members

        protected EstudioInfo CurrentStudio = null;
        protected UsuarioInfo CurrentUser = null;

        /// <summary>
        /// Logger instance
        /// </summary>
        //public static ILog Logger { get; set; }

        /// <summary>
        /// Entities stored in cache memory
        /// </summary>
        private static readonly Dictionary<Type, string> CachedEntities = new Dictionary<Type, string>()
        {
            // TODO => "ESTUDIO" should not be a cached entity, since this recordset may start to grow...
            { typeof (Estudio), WellKnownKeys.Cache.ESTUDIOS },
            { typeof (UsuarioCategoria), WellKnownKeys.Cache.CATEGORIAS_USUARIO },
            { typeof (GuionEstado), WellKnownKeys.Cache.ESTADOS_GUION },
            { typeof (PagoEstado), WellKnownKeys.Cache.ESTADOS_PAGO },
            { typeof (ReclamoEstado), WellKnownKeys.Cache.ESTADOS_RECLAMO },
            { typeof (PagoMedio), WellKnownKeys.Cache.MEDIOS_PAGO },
            { typeof (TipoCancion), WellKnownKeys.Cache.TIPOS_CANCION },
            { typeof (TipoDocumento), WellKnownKeys.Cache.TIPOS_DOCUMENTO },
            { typeof (ProyectoTipo), WellKnownKeys.Cache.TIPOS_PROYECTO },
            { typeof (UsuarioTipo), WellKnownKeys.Cache.TIPOS_USUARIO },
            { typeof (AutorizacionesGuion), WellKnownKeys.Cache.AUTORIZACIONES_GUION },
            { typeof (NotificacionTipo), WellKnownKeys.Cache.TIPOS_NOTIFICACION },
            { typeof (DireccionTipo), WellKnownKeys.Cache.TIPOS_DIRECCION },
            { typeof (TelefonoTipo), WellKnownKeys.Cache.TIPOS_TELEFONO }
        };

        protected static readonly int[] IdsTiposUsuarioManager =
            new int[]
            {
                WellKnownKeys.Entities.TiposUsuario.MANAGER.Id,
                WellKnownKeys.Entities.TiposUsuario.PRODUCTOR.Id
            };
        
        #endregion

        #region Events

        protected void Page_Load(object sender, EventArgs e)
        {
            //this.ConfigureLogger();
            
            this.Error += new System.EventHandler(this.Page_Error);

            if (Helpers.SecurityHelper.IsLoginPage()) 
                return;

            var currentSession = Helpers.SessionHelper.GetCurrentSessionInfo();
            if (currentSession != null)
            {
                this.CurrentStudio = currentSession.Studio;
                this.CurrentUser = currentSession.User;
            }
            else
            {
                Helpers.SecurityHelper.RedirectUserToLoginPage();
            }
        }

        //private void ConfigureLogger()
        //{
        //    if (Logger != null)
        //        return;
        //    Logger = LogManager.GetLogger(Assembly.GetExecutingAssembly().GetTypes().First());
        //    log4net.Config.XmlConfigurator.Configure();
        //}

        public void Page_Error(object sender, EventArgs e)
        {
            var ex = HttpContext.Current.Server.GetLastError();
            LogException(ex);
        }

        #endregion

        public static List<ListaInfo> GetListInfo<T, TModel>(Func<T, bool> filterCriteria = null, Func<T, object> sortCriteria = null ) where T:Entidad
        {
            var resultados = new ControladorBase<T>().ObtenerEntidades(filterCriteria);
            if (resultados == null)
                return new List<ListaInfo>();

            if (sortCriteria == null)
                sortCriteria = x => x.Descriptor;

            var orderedResults = resultados.OrderBy(sortCriteria);

            return GetListInfo<T, TModel>(orderedResults);
        }
        public static List<ListaInfo> GetListInfo<T, TModel>(IEnumerable<T> data) where T:Entidad
        {
            return data.Select
                (
                    x =>
                        new ListaInfo
                        {
                            Id = x.Id,
                            Texto = x.Descriptor,
                            Habilitado = x.Activo,
                            Valor = x.GetModel<T, TModel>()
                        }
                ).ToList();            
        }

        public static int ParseParameterAsEntityId(string idParameter)
        {
            var id = WellKnownKeys.Entities.DEFAULT_ID;

            Int32.TryParse(idParameter, out id);
            return id == 0 ? WellKnownKeys.Entities.DEFAULT_ID : id;
        }

        #region << Get Users With Active Contract >>
        private static PagedData<Usuario> GetUsersWithActiveContract(int idEstudio, Requests.Search.SearchUserRequest searchUserRequest = null, int? pageSize = null, int? currentPage = null, string sortColumn = "Id", string sortType = "asc")
        {
            var storedProcedureName = WellKnownKeys.StoredProcedures.USUARIOS_CON_VINCULO_ACTIVO_GET;

            object id_usuario_categoria = DBNull.Value;
            object id_usuario_tipo = DBNull.Value;
            object usuario_tipo = DBNull.Value;
            object usuario_categoria = DBNull.Value;
            object nombre = DBNull.Value;
            object apellido = DBNull.Value;

            if (searchUserRequest != null)
            {
                if (!String.IsNullOrEmpty(searchUserRequest.Nombre))
                    nombre = searchUserRequest.Nombre;
                
                if (!String.IsNullOrEmpty(searchUserRequest.Apellido))
                    apellido = searchUserRequest.Apellido; 

                if (searchUserRequest.IdCategoria.HasValue)
                    id_usuario_categoria = searchUserRequest.IdCategoria.Value; 

                if (searchUserRequest.IdTipo.HasValue)
                    id_usuario_tipo = searchUserRequest.IdTipo.Value; 

                if (!String.IsNullOrEmpty(searchUserRequest.Tipo))
                    usuario_tipo = searchUserRequest.Tipo; 

                if (!String.IsNullOrEmpty(searchUserRequest.Categoria))
                    usuario_categoria = searchUserRequest.Categoria; 
            }

            var parameters = new Common.Data.ParameterList();
                parameters.AddParameter("id_estudio", idEstudio);
                parameters.AddParameter("id_usuario_categoria", id_usuario_categoria);
                parameters.AddParameter("id_usuario_tipo", id_usuario_tipo);
                parameters.AddParameter("usuario_tipo", usuario_tipo);
                parameters.AddParameter("usuario_categoria", usuario_categoria);
                parameters.AddParameter("nombre", nombre);
                parameters.AddParameter("apellido", apellido);

            var data = default(IList<Usuario>);
            var results= default(PagedData<Usuario>);

            var _currentPage = 0;
            var _totalPages = 0;
            var _totalRecords = 0;

            if (currentPage.HasValue && pageSize.HasValue)
            {
                var totalRecords    = new ControladorBase<Entidades.Calculado.TotalRecords>.StoredProcedure().GetTotalRecords(String.Format("{0}_count", storedProcedureName), parameters.ToDictionary());
                var pagedData       = GridDataHelper.GetPaginationData(totalRecords, currentPage.Value, pageSize.Value);

                parameters.AddParameter("sort_column", sortColumn);
                parameters.AddParameter("sort_type", sortType);

                data = new ControladorBase<Usuario>.StoredProcedure().GetList(storedProcedureName, pageSize.Value, currentPage.Value, parameters.ToDictionary());

                _currentPage = pagedData.CurrentPage;
                _totalPages = pagedData.TotalPages;
                _totalRecords = pagedData.TotalRecords;
            }
            else
            {
                parameters.AddParameter("page_size", DBNull.Value, 0);
                parameters.AddParameter("current_page", DBNull.Value, 1);

                parameters.AddParameter("sort_column", sortColumn);
                parameters.AddParameter("sort_type", sortType);

                data = new StoredProcedure<Usuario>().GetList(storedProcedureName, parameters.ToDictionary());

                _currentPage = 1;
                _totalPages = 1;
                _totalRecords = data.Count;
            }

            results = new PagedData<Usuario>()
            {
                CurrentPage = _currentPage,
                TotalPages = _totalPages,
                TotalRecords = _totalRecords,
                //Data = data.Select(contratacion => contratacion.Usuario).ToList()
                Data = data.ToList()
            };

            return results;
        }
        public static List<Usuario> ObtenerUsuariosConVinculoActivo(int idEstudio, Requests.Search.SearchUserRequest searchUserRequest = null)
        {
            var pagedData = GetUsersWithActiveContract(idEstudio, searchUserRequest);
            return pagedData.Data;
        }
        public static PagedData<Usuario> ObtenerUsuarios(int idEstudio, int pageSize, int currentPage, Requests.Search.SearchUserRequest searchUserRequest, string sortColumn, string sortType)
        {
            var pagedData = GetUsersWithActiveContract(idEstudio, searchUserRequest, pageSize, currentPage, sortColumn, sortType);
            return pagedData;
        }
        #endregion

        #region Logging
        // TODO => Do this the right way!
        public static void LogMessage(string message, string details = null)
        {
            new ControladorBase<Error>().GuardarEntidad(new Error()
            {
                FechaYHora = DateTime.Now,
                Message = message,
                ExceptionType = String.Empty,
                StackTrace = String.IsNullOrEmpty(details) ? String.Empty : details
            });               
        }
        public static void LogError(Exception ex)
        {
            LogException(ex);
        }
        private static void LogException(Exception exception)
        {
            try
            {
                //Logger.Error( String.Format("Source: {0}, Method: {1}, Exception Type: {2}, Message: {3}", exception.TargetSite.DeclaringType.FullName, exception.TargetSite.Name, exception.GetType(), exception.Message));

                // Get the last exception thrown
                //var ex = Server.GetLastError();
                //var exception = HttpContext.Current.Server.GetLastError();

                //Log
                var exceptionDetails = exception.GetExceptionDetails();
                new ControladorBase<Error>().GuardarEntidad(new Error()
                {
                    FechaYHora = DateTime.Now,
                    //Message = exception.Message,
                    Message = exceptionDetails,
                    ExceptionType = exception.GetType().ToString(),
                    StackTrace = exception.StackTrace
                });            

                // Clear the error from the server
                //Server.ClearError();
                HttpContext.Current.Server.ClearError();
            }
            catch (Exception)
            {
                //throw;
            }
        }
        #endregion

        #region Web Methods

        #region Sesion

        /// <summary>
        /// Abre una nueva sesión
        /// </summary>
        /// <param name="nombreUsuario"></param>
        /// <param name="password"></param>
        /// /// <param name="idEstudio"></param>
        /// <returns></returns>
        [WebMethod]
        public static string abrirSesion(string nombreUsuario, string password, int idEstudio)
        {
            var mensaje = string.Empty;
            var resultado = false;
            object valor = null;
            try
            {
                var controladorUsuario = new ControladorBase<Usuario>();
                var controladorContratacion = new ControladorBase<Contratacion>();

                var usuario = controladorUsuario.ObtenerEntidad(x => x.NombreUsuario.ToLower() == nombreUsuario.ToLower() && x.Password.ToLower() == password.ToLower());
                if (usuario != null)
                {
                    var usuarioInfo = new UsuarioInfo(usuario);
                    var ultimaContratacion = controladorContratacion.ObtenerEntidad(x => x.Activo && x.Estudio.Id == idEstudio && x.Usuario.Id == usuario.Id);
                    if (ultimaContratacion != null)
                    {
                        usuarioInfo.Contratacion = new ContratacionInfo(ultimaContratacion);

                        valor       = usuarioInfo;
                        mensaje     = usuario.Nombre + " ha iniciado sesión.";
                        resultado   = true;

                        var estudioInfo = new EstudioInfo(ultimaContratacion.Estudio);

                        var parametro = new ControladorBase<Parametro>().ObtenerEntidad(x => x.Estudio.Id == estudioInfo.Id);
                        var parametroInfo = new ParametroInfo(parametro);

                        SessionHelper.SetCurrentSessionInfo(new SessionInfo()
                        {
                            User = usuarioInfo,
                            Studio = estudioInfo,
                            Settings = parametroInfo
                        });
                    }
                    else
                    {
                        mensaje = "El Usuario no tiene acceso al Estudio seleccionado.";
                    }
                }
                else
                {
                    mensaje = "Los datos son incorrectos.";
                }
            }
            catch (Exception ex)
            {
                //throw ex;
                mensaje += Environment.NewLine + ex.Message;
            }
            return SessionHelper.BuildResponse(mensaje, resultado, valor);
        }

        /// <summary>
        /// Abre una nueva sesión
        /// </summary>
        /// <returns></returns>
        [WebMethod]
        public static string cerrarSesion()
        {
            var mensaje = "La Sesión no pudo ser cerrada.";
            var resultado = false;
            object valor = null;
            try
            {
                if (SessionHelper.LogOff())
                {
                    mensaje = "Sesión cerrada";
                    resultado = true;
                }
            }
            catch (Exception ex)
            {
                LogError(ex);
                mensaje = ex.Message;
            }
            return SessionHelper.BuildResponse(mensaje, resultado, valor);
        }

        /// <summary>
        /// Redirige a la página inicial del Usuario
        /// </summary>
        /// <param name="idTipoUsuario"></param>
        /// <param name="idCategoriaUsuario"></param>
        [WebMethod]
        public static string cargarPaginaInicial(int idTipoUsuario, int idCategoriaUsuario)
        {
            var mensaje     = string.Empty;
            var resultado   = false;
            object valor    = null;
            try
            {
                mensaje     = "OK";
                resultado   = true;
                valor       = Helpers.SecurityHelper.GetDefaultPage(idTipoUsuario, idCategoriaUsuario);
            }
            catch (Exception ex)
            {
                //throw ex;
                mensaje = "Hubo un error al intentar cargar la página inicial." + Environment.NewLine + ex.Message;
            }
            return SessionHelper.BuildResponse(mensaje, resultado, valor);
        }

        #endregion

        #region Usuario
        
        /// <summary>
        /// Devuelva una lista de todos los Usuarios
        /// </summary>
        /// <param name="pageSize"></param>
        /// <param name="currentPage"></param>
        /// <param name="sortColumn"></param>
        /// <param name="sortOrder"></param>
        /// <param name="search"></param>
        /// <param name="searchFilter"></param>
        /// <param name="colModel"></param>
        /// <returns></returns>
        [WebMethod]
        public static string obtenerUsuarios(int pageSize, int currentPage, string sortColumn, string sortOrder, string search, string searchFilter, string colModel)
        {
            var jSON = string.Empty;
            try
            {
                //var gridData = new GridDataHelper<Usuario>().GetData(searchFilter, colModel, currentPage, pageSize, sortOrder, sortColumn);
                var idEstudio = SessionHelper.GetCurrentSessionInfo().Studio.Id;

                var parameters = GridDataHelper.GetParameters(searchFilter, colModel).ToDictionary();

                var searchUserRequest = new Requests.Search.SearchUserRequest();

                if (parameters.ContainsKey("Nombre"))
                    searchUserRequest.Nombre = parameters["Nombre"].ToString();
                if (parameters.ContainsKey("Apellido"))
                    searchUserRequest.Apellido = parameters["Apellido"].ToString();
                if (parameters.ContainsKey("Tipo.Id"))
                    searchUserRequest.IdTipo = Convert.ToInt32(parameters["Tipo.Id"]);
                if (parameters.ContainsKey("Tipo.Nombre"))
                    searchUserRequest.Tipo = parameters["Tipo.Nombre"].ToString();
                if (parameters.ContainsKey("Categoria.Id"))
                    searchUserRequest.IdCategoria = Convert.ToInt32(parameters["Categoria.Id"]);
                if (parameters.ContainsKey("Categoria.Nombre"))
                    searchUserRequest.Categoria = parameters["Categoria.Nombre"].ToString();

                var gridData = ObtenerUsuarios(idEstudio, pageSize, currentPage, searchUserRequest, sortColumn, sortOrder);

                #region Construir set de datos

                var lista = from Usuario item in gridData.Data
                    select new object[]
                    {
                        item.Id.ToString(),
                        item.Activo,
                        Helpers.HtmlHelper.CrearBoton(WellKnownKeys.Grid.Actions.EDITAR, "Usuario", item.Id,
                            WellKnownKeys.Images.Icons.EDITAR),
                        item.Activo
                            ? Helpers.HtmlHelper.CrearBoton(WellKnownKeys.Grid.Actions.ELIMINAR, "Usuario", item.Id,
                                WellKnownKeys.Images.Icons.ELIMINAR)
                            : "",
                        item.Tipo.Id.ToString(),
                        item.Tipo.Nombre,
                        //item.Categoria.Id.ToString(),
                        //item.Categoria.Nombre,
                        item.Nombre,
                        item.Apellido,
                        item.Categoria.Id,
                        item.Categoria.Nombre
                    };

                var jqGridData = new jqGridData(gridData.TotalRecords, gridData.TotalPages, gridData.CurrentPage, lista);

                #endregion

                jSON = Formatter.JSON<jqGridData>.Serialize(jqGridData);
            }
            catch (Exception ex)
            {
                LogError(ex);
                throw;
            }
            return jSON;
        }

        /// <summary>
        /// Elimina los datos de un Usuario
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [WebMethod]
        public static string eliminarUsuario(int id)
        {
            string mensaje  = string.Empty;
            bool resultado  = false;
            object valor    = null;
            try
            {
                resultado = ActivarEntidad<Usuario>( id, false);
                mensaje = resultado ? "Los datos del Usuario fueron eliminados exitosamente." : "Hubo un error al intentar eliminar los datos del Usuario.";
            }
            catch (Exception ex)
            {
                //throw ex;
                mensaje = "Hubo un error al intentar eliminar los datos del Usuario." + Environment.NewLine + ex.Message;
            }
            return SessionHelper.BuildResponse(mensaje, resultado, valor);
        }

        #endregion

        #region Proyecto

        /// <summary>
        /// Elimina los datos de un Proyecto
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [WebMethod]
        public static string eliminarProyecto(int id)
        {
            var mensaje     = "Ocurrió un error al intentar eliminar los datos del Proyecto.";
            var resultado   = false;
            object valor    = null;
            try
            {
                var controladorProyecto = new ControladorBase<Proyecto>();
                
                var proyecto = controladorProyecto.ObtenerEntidad(id);
                    proyecto.Activo = false;
                    proyecto.Guiones.ToList().ForEach(x => x.Activo = false);
                
                resultado = controladorProyecto.GuardarEntidad( proyecto );

                mensaje = "Los datos del Proyecto fueron eliminados exitosamente.";
            }
            catch (Exception ex)
            {
                LogError(ex);
                mensaje += Environment.NewLine + "Detalle: " + ex.Message;
            }
            return SessionHelper.BuildResponse(mensaje, resultado, valor);
        }

        #endregion

        #region Guion

        /// <summary>
        /// Elimina los datos de un Guión
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [WebMethod]
        public static string eliminarGuion(int id)
        {
            var mensaje  = string.Empty;
            var resultado  = false;
            object valor    = null;
            try
            {
                var controladorGuion = new ControladorBase<Guion>();

                var guion = controladorGuion.ObtenerEntidad( id );
                if (guion == null)
                    throw new Exception("No existe el Guión solicitado.");

                var currentSession = SessionHelper.GetCurrentSessionInfo();
                var canDelete = BusinessRulesHelper.Guion.CanDelete(guion, currentSession.User);
                if (!canDelete)
                {
                    mensaje = "No tiene permisos para realizar esta acción.";
                }
                else
                {
                    guion.Activo = false;
                    resultado = controladorGuion.GuardarEntidad( guion );

                    var controladorGuionImportacion = new ControladorBase<GuionImportacion>();
                    var guionImportacion = controladorGuionImportacion.ObtenerEntidad(x => x.Guion.Id == guion.Id);
                    if (guionImportacion != null)
                    {
                        guionImportacion.Activo = false;
                        resultado = controladorGuionImportacion.GuardarEntidad( guionImportacion );
                    }

                    resultado = true;
                    mensaje = "Los datos del Guión fueron eliminados exitosamente.";
                }
            }
            catch (Exception ex)
            {
                //throw ex;
                mensaje = "Hubo un error al intentar eliminar los datos del Guión." + Environment.NewLine + ex.Message;
            }
            return SessionHelper.BuildResponse(mensaje, resultado, valor);
        }

        /// <summary>
        /// Devuelve los Guiones con Estado = PENDIENTE_DE_PAGO
        /// </summary>
        /// <param name="idProyecto"></param>
        /// <returns></returns>
        [WebMethod]
        public static string obtenerGuionesPendientesDePago(int idProyecto)
        {
            var mensaje = string.Empty;
            var resultado = false;
            object valor = null;
            try
            {
                var guionesInfo = new List<ListaInfo>();
                var guiones = new GuionControlador().ObtenerGuionesPendientesDePago(idProyecto, WellKnownKeys.Entities.TiposUsuario.ACTOR.Id);
                if (guiones.IsNotNull())
                {
                    guionesInfo.AddRange(
                        guiones.Select(
                            guion =>
                                new ListaInfo()
                                {
                                    Id = guion.Id,
                                    Texto = String.Format("Ep. {0} - {1}", guion.Episodio, guion.Titulo)
                                }));
                    valor = guionesInfo;
                    resultado = true;
                }
                else
                {
                    mensaje = "No se han podido obtener el listado de Guiones.";
                    resultado = false;
                }   
            }
            catch (Exception ex)
            {
                //throw ex;
                mensaje = "Hubo un error al intentar obtener el Detalle de la Planilla de Pago." + Environment.NewLine + ex.Message;
            }
            return SessionHelper.BuildResponse(mensaje, resultado, valor);
        }

        /// <summary>
        /// Devuelve una Transición de Autorización de Guión
        /// </summary>
        /// <param name="idTransicionAutorizacion"></param>
        /// <returns></returns>
        [WebMethod]
        public static string obtenerTransicionAutorizacion(int idTransicionAutorizacion)
        {
            var respuestaInfo = default(RespuestaInfo);
            var mensaje = String.Empty;
            var valor = default(object);
            var resultado = false;
            try
            {
                var controlador = new ControladorBase<GuionAutorizacionTransicion>();

                var transicionAutorizacion = controlador.ObtenerEntidad(x => x.Id == idTransicionAutorizacion);
                if (transicionAutorizacion != null)
                {
                    var autorizacionTransicionInfo = new GuionAutorizacionTransicionInfo(transicionAutorizacion);

                    mensaje = "Autorización obtenida exitosamente.";
                    resultado = true;
                    valor = autorizacionTransicionInfo;

                    respuestaInfo = new RespuestaInfo(resultado, mensaje, valor);
                }
            }
            catch (Exception ex)
            {
                //throw ex;
                LogError(ex);

                respuestaInfo = new RespuestaInfo(resultado, mensaje, valor);
            }
            return SessionHelper.BuildResponse(respuestaInfo);
        }

        /// <summary>
        /// Actualiza una Autorización de Guión
        /// </summary>
        /// <param name="autorizacionTransicionInfo"></param>
        /// <returns></returns>
        [WebMethod]
        public static string resolverAutorizacion(GuionAutorizacionTransicionInfo autorizacionTransicionInfo)
        {
            var respuestaInfo = default(RespuestaInfo);
            try
            {
                var controladorGuion = new ControladorBase<Guion>();
                var controladorUsuario = new ControladorBase<Usuario>();

                var currentSession = Helpers.SessionHelper.GetCurrentSessionInfo();
                var usuarioActual = controladorUsuario.ObtenerEntidad(x => x.Id == currentSession.User.Id);
                var usuarioNotificado = controladorUsuario.ObtenerEntidad(x => x.Id == autorizacionTransicionInfo.IdUsuarioNotificado);
                var fechaActual = DateTime.Now;

                var guion = controladorGuion.ObtenerEntidad(x => x.Id == autorizacionTransicionInfo.IdGuion);

                var aprobada = false;
                if (autorizacionTransicionInfo.Aprobada.HasValue)
                    aprobada = autorizacionTransicionInfo.Aprobada.Value;

                GuionControlador.ActualizarTransicionDeAutorizacion(guion, autorizacionTransicionInfo.Id, aprobada, autorizacionTransicionInfo.Comentarios, fechaActual, usuarioActual, usuarioNotificado);

                var idAutorizacion = guion.TransicionDeAutorizaciones.Last().Autorizacion.Id;

                switch (idAutorizacion)
                {
                    case WellKnownKeys.Entities.AutorizacionesGuion.TRADUCCION.Id:
                        if (aprobada)
                            GuionControlador.AgregarTransicionDeAutorizacion(guion, WellKnownKeys.Entities.AutorizacionesGuion.PRODUCCION.Id, fechaActual, usuarioActual.Id);
                        break;
                    case WellKnownKeys.Entities.AutorizacionesGuion.PRODUCCION.Id:
                        break;
                }

                if (controladorGuion.GuardarEntidad(guion))
                {
                    respuestaInfo = new RespuestaInfo(true, "Autorización resuelta exitosamente.", autorizacionTransicionInfo);

                    if (usuarioNotificado != null)
                    {
                        var destinatarios = MailHelper.CreateRecipientsList(usuarioNotificado.Email, usuarioNotificado.Descriptor);

                        Helpers.MailHelper.EnviarMailSolicitudAutorizacionGuion
                            (
                                destinatarios,
                                String.Format("{0} - Ep. {1}", guion.Titulo, guion.Episodio),
                                guion.TransicionDeAutorizaciones.Last().Autorizacion.Nombre,
                                String.Format("{0}, {1}", currentSession.User.Apellido, currentSession.User.Nombre),
                                usuarioNotificado.Descriptor
                            );
                    }
                }
            }
            catch (Exception ex)
            {
                //throw ex;
                LogError(ex);

                respuestaInfo = new RespuestaInfo(false, "Ocurrió un error al intentar resolver la Autorización. Detalle: " + ex.Message, null);
            }
            
            return SessionHelper.BuildResponse(respuestaInfo);
        }

        /// <summary>
        /// Actualiza el estado del proceso de importación de un Guión
        /// </summary>
        /// <param name="idTranslator"></param>
        /// <param name="firstName"></param>
        /// <param name="lastName"></param>
        /// <returns></returns>
        [WebMethod]
        public static string continueScriptImportProcess(int idTranslator, string firstName, string lastName)
        {
            var respuestaInfo = default(RespuestaInfo);
            try
            {
                var context = Helpers.WebHelper.GetCurrentContext();

                var importProject = ScriptHelper.GetImportCurrentState(context, String.Empty);

                importProject.Data.IdTranslator = idTranslator;
                importProject.Data.TranslatorFirstName = firstName;
                importProject.Data.TranslatorLastName = lastName;
                
                ScriptHelper.SaveImportCurrentState(context, importProject, ImportStep.ReadyForImport);

                respuestaInfo = ScriptHelper.ContinueImportProcess(importProject, context);
            }
            catch (Exception ex)
            {
                //throw ex;
                LogError(ex);

                respuestaInfo = ScriptHelper.GetResponseInfoWithExceptionData(ex);
            }

            return SessionHelper.BuildResponse(respuestaInfo);
        }

        #endregion

        #region Reclamo

        /// <summary>
        /// Envía un Reclamo
        /// </summary>
        /// <param name="idPago"></param>
        /// <param name="idReclamo"></param>
        /// <param name="idEstado"></param>
        /// <param name="mensaje"></param>
        /// <returns></returns>
        [WebMethod]
        public static string enviarReclamo(int idReclamo, int idPago, int idEstado, string mensaje)
        {
            var _mensaje     = string.Empty;
            var resultado   = false;
            object valor    = null;
            try
            {
                var controladorReclamo          = new ControladorBase<Reclamo>();
                var controladorReclamoEstado    = new ControladorBase<ReclamoEstado>();
                var controladorUsuario          = new ControladorBase<Usuario>();

                var currentSession = SessionHelper.GetCurrentSessionInfo();
                var emisorEsProveedor = !Helpers.SecurityHelper.HasManagerRights(currentSession.User);

                var reclamo = default(Reclamo);

                var concepto = String.Empty;

                if (idReclamo == WellKnownKeys.Entities.DEFAULT_ID)
                {
                    concepto = "El reclamo";

                    reclamo = new Reclamo
                    {
                        Estado  = controladorReclamoEstado.ObtenerEntidad(WellKnownKeys.Entities.EstadosReclamo.SIN_ATENDER.Id),
                        Fecha   = DateTime.Now,
                        Pago    = new ControladorBase<Pago>().ObtenerEntidad(idPago),
                        Items   = new List<ReclamoItem>()
                    };
                }
                else
                {
                    concepto = "La respuesta del reclamo";

                    reclamo = controladorReclamo.ObtenerEntidad(idReclamo);
                    reclamo.Estado = controladorReclamoEstado.ObtenerEntidad(idEstado);
                }

                var reclamoItem = new ReclamoItem
                {
                    Mensaje = mensaje,
                    Fecha   = DateTime.Now,
                    Reclamo = reclamo,
                    Emisor  = controladorUsuario.ObtenerEntidad(currentSession.User.Id)
                };

                reclamo.Items.Add(reclamoItem);

                resultado = controladorReclamo.GuardarEntidad(reclamo);
                if (resultado)
                {
                    _mensaje = concepto + " se envió exitosamente.";
                    MailHelper.EnviarMailReclamo(reclamo, emisorEsProveedor);
                }
                else
                {
                    _mensaje = concepto + " no se pudo enviar.";
                }
            }
            catch (Exception ex)
            {
                //throw ex;
                var detalle = ex.Message;
                if ( ex.InnerException != null )
                    detalle = detalle + Environment.NewLine + ex.InnerException.Message;
                _mensaje = "Hubo un error al intentar realizar el envío." + Environment.NewLine + "Detalle: " + Environment.NewLine + detalle;
            }
            return SessionHelper.BuildResponse(_mensaje, resultado, valor);
        }

        /// <summary>
        /// Devuelve la info de un Reclamo
        /// </summary>
        /// <param name="idReclamo"></param>
        /// <returns></returns>
        [WebMethod]
        public static string obtenerReclamo(int idReclamo)
        {
            var mensaje  = string.Empty;
            var resultado  = false;
            object valor    = null;
            try
            {
                var reclamo = new ControladorBase<Reclamo>().ObtenerEntidad( idReclamo );
                if ( reclamo != null )
                {
                    valor       = new ReclamoInfo( reclamo );
                    resultado   = true;
                }
                else
                {
                    throw new Exception( "No se ha podido obtener los datos del Reclamo." );
                }
            }
            catch (Exception ex)
            {
                //throw ex;
                mensaje = "Hubo un error al intentar obtener los datos del Reclamo." + Environment.NewLine + ex.Message;
            }
            return SessionHelper.BuildResponse(mensaje, resultado, valor);
        }

        #endregion

        #region Planillas

        [WebMethod]
        public static string generarPlanilla(string infoPlanilla, string infoPlanillaItems)
        {
            var mensaje = String.Empty;
            var resultado = false;
            object valor = null;
            try
            {                                
                var planillaInfo = JsonConvert.DeserializeObject<PlanillaInfo>(infoPlanilla);

                switch ( planillaInfo.TipoPlanilla )
                {
                    case TiposPlanilla.Actores:
                    case TiposPlanilla.Staff:
                        var planillaPagoInfo = Helpers.PlanillasHelper.DeserializarPlanillaDePagoInfo(infoPlanilla, infoPlanillaItems);
                        var idsPago = planillaPagoInfo.Items.Select(x => x.IdPago).ToArray();
                        var pagosRealizados = Helpers.FacturacionHelper.RealizarPagos(idsPago, planillaPagoInfo.IdMedioPago);
                        var idsPagosRealizadosOk = pagosRealizados.Where(x => x.Value).Select(x => x.Key).ToArray();
                        planillaPagoInfo.Items = planillaPagoInfo.Items.Where(x => idsPagosRealizadosOk.Contains(x.IdPago) ).ToList();
                        mensaje = String.Format("Planilla generada" + "<br /><br />" + "Pagos actualizados: {0}/{1}", idsPagosRealizadosOk.Length, pagosRealizados.Count);
                        break;

                    case TiposPlanilla.Credito:
                        // TODO => WTF ???
                        //var planillaCreditosInfo = Helpers.PlanillasHelper.DeserializarPlanillaDeCreditosInfo(infoPlanilla, infoPlanillaItems);
                        mensaje = "Planilla generada" + "<br />";
                        break;
                }

                var fileNamePlanilla = Helpers.PlanillasHelper.GenerarPlanilla( infoPlanilla, infoPlanillaItems );

                // TODO => make this config key a Constant
                var appHost = ConfigurationManager.AppSettings["appHost"];

                if (!String.IsNullOrEmpty(appHost))
                {
                    fileNamePlanilla = fileNamePlanilla.Replace( Helpers.FileHelper.GetServerRoot(), appHost );
                }

                valor = fileNamePlanilla;
                resultado = true;
            }
            catch (Exception ex)
            {
                //throw ex;
                LogError(ex);
                mensaje = "Hubo un error al intentar generar la Planilla.";
            }
            return SessionHelper.BuildResponse(mensaje, resultado, valor);
        }

        #endregion

        #endregion

        #region Entities

        public class Estudios : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                return ObtenerTodos<Estudio, EstudioInfo>();
            }
        }
        public class EstadosReclamo : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                return ObtenerTodos<ReclamoEstado, ReclamoEstadoInfo>();
            }
        }
        public class TiposUsuario : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                //return ObtenerTodos<UsuarioTipo, UsuarioTipoInfo>();
                return CacheEntity.ObtenerTodos<UsuarioTipo, UsuarioTipoInfo>(x => !IdsTiposUsuarioManager.Contains(x.Id));
            }
        }
        public class CategoriasUsuario : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                return ObtenerTodos<UsuarioCategoria, UsuarioCategoriaInfo>();
            }
        }
        public class TiposProyecto : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                return ObtenerTodos<ProyectoTipo, ProyectoTipoInfo>();
            }
        }
        public class EstadosGuion : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                //return ObtenerTodos<GuionEstado, GuionEstadoInfo>();
                return ObtenerTodos<GuionEstado, GuionEstadoInfo>(null, x => x.Id);
            }
        }
        public class AutorizacionesGuion : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                return ObtenerTodos<GuionAutorizacion, GuionAutorizacionInfo>();
            }
        }
        public class MediosPago : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                return ObtenerTodos<PagoMedio, PagoMedioInfo>();
            }
        }
        public class EstadosPago : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                return ObtenerTodos<PagoEstado, PagoEstadoInfo>();
            }
        }
        public class TiposCancion : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                return ObtenerTodos<TipoCancion, TipoCancionInfo>();
            }
        }
        public class TiposDocumento : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                return ObtenerTodos<TipoDocumento, TipoDocumentoInfo>();
            }
        }
        public class TiposNotificacion : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                return ObtenerTodos<NotificacionTipo, NotificacionTipoInfo>();
            }
        }
        public class TiposDireccion : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                return ObtenerTodos<DireccionTipo, DireccionTipoInfo>();
            }
        }
        public class TiposTelefono : CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos()
            {
                return ObtenerTodos<TelefonoTipo, TelefonoTipoInfo>();
            }
        }

        #region Private Methods
        private static bool GuardarEntidad<T>(ControladorBase<T> controlador, T entidad) where T:Entidad
        {
            controlador.GuardarEntidad(entidad);

            Cache.Update<T>();

            return true;
        }
        private static bool EliminarEntidad<T>(ControladorBase<T> controlador, T entidad, string key) where T:Entidad
        {
            controlador.EliminarEntidad(entidad);
            Helpers.WebHelper.Cache.RemoveValue(key);
            return true;
        }
        private static bool ActivarEntidad<T>(int id, bool activa = true) where T:Entidad
        {
            var resultado = false;
            try
            {
                var controlador = new ControladorBase<T>();
                var entidad = controlador.ObtenerEntidad(id);
                if ( entidad.IsNotNull() )
                {
                    entidad.Activo = activa;
                    resultado = GuardarEntidad(controlador, entidad);
                }
            }
            catch (Exception)
            {
                throw;
            }
            return resultado;
        }
        #endregion

        #endregion

        #region Caché
        public class CacheEntity
        {
            public static List<ListaInfo> ObtenerTodos<T, TModel>(Func<T, bool> filterCriteria = null, Func<T, object> sortCriteria = null ) where T:Entidad
            {
                List<ListaInfo> entidades;
                try
                {
                    entidades = Cache.Get<T, List<ListaInfo>>();
                    if (entidades.IsNull())
                    {
                        //var controlador = new ControladorBase<T>();
                        //var datos = criteria.IsNotNull() ? controlador.ObtenerEntidades(criteria) : controlador.ObtenerEntidades();
                        if ( filterCriteria.IsNull() )
                            filterCriteria = x => x.Activo;
                        var datos = new ControladorBase<T>().ObtenerEntidades(filterCriteria);
                        if (datos.IsNotNull())
                        {
                            //datos = datos.OrderBy(x => x.Descriptor).ToList();
                            if (sortCriteria == null )
                                sortCriteria = x => x.Descriptor;
                            datos = datos.OrderBy(sortCriteria).ToList();

                            entidades = datos.Select
                                        (
                                            x =>
                                                new ListaInfo
                                                {
                                                    Id          = x.Id,
                                                    Texto       = x.Descriptor,
                                                    Habilitado  = x.Activo,
                                                    Valor       = x.GetModel<T, TModel>()
                                                }
                                        ).ToList();
                            Cache.Update<T>(entidades);
                        }
                    }
                }
                catch (Exception)
                {
                    throw;
                }
                return entidades;
            }        
        }
        public new class Cache
        {
            public static TReturnType Get<T, TReturnType>() where T: Entidad
            {
                var key = GetKey<T>();
                return Helpers.WebHelper.Cache.GetValue<TReturnType>(key);
            }
            public static T Get<T>(string key)
            {
                return Helpers.WebHelper.Cache.GetValue<T>(key);
            }
            public static void Update<T>(object data = null) where T: Entidad
            {
                var key = GetKey<T>();
                if ( key.IsNotNull() && key.Length > 0 )
                {
                    if (data.IsNotNull())
                        Helpers.WebHelper.Cache.SetValue<object>(key, data);
                    else
                        Helpers.WebHelper.Cache.RemoveValue(key);
                }
            }
            private static KeyValuePair<Type, string> GetCacheKeyValuePairFromDictionary<T>() where T:Entidad
            {
                return CachedEntities.GetKeyValuePair(typeof (T));
            }
            public static string GetKey<T>() where T:Entidad
            {
                var keyValuePair = GetCacheKeyValuePairFromDictionary<T>();
                if ( keyValuePair.IsNotNull() && keyValuePair.Key.IsNotNull() )
                    return keyValuePair.Value;
                else
                    return null;
            }
        }
        #endregion

        #region Grid
        public static string SerializeGridData(jqGridData jqGridData)
        {
            return Formatter.JSON<jqGridData>.Serialize(jqGridData);
        }
        public static jqGridData GetDefaultGridData()
        {
            return new jqGridData(0, 1, 1, null);
        }
        public class Grid<T> where T:Entidad
        {
            #region Public Methods
            public jqGridData GetGridData(string searchFilter, string colModel, int currentPage, int pageSize, string sortOrder, string sortColumn, Func<IList<T>, IEnumerable<object[]>> buildDataRowsFunc)
            {
                var gridData = new GridDataHelper<T>().GetData(searchFilter, colModel, currentPage, pageSize, sortOrder, sortColumn);
                var dataRows = buildDataRowsFunc(gridData.Data);

                return BuildGridData(gridData, dataRows);
            }
            public jqGridData GetGridData(string storedProcedureName, Dictionary<string, object> parameters, int currentPage, int pageSize, Func<IList<T>, IEnumerable<object[]>> buildDataRowsFunc)
            {
                var totalRecords    = new ControladorBase<Entidades.Calculado.TotalRecords>.StoredProcedure().GetTotalRecords(String.Format("{0}_count", storedProcedureName), parameters);
                var data            = new ControladorBase<T>.StoredProcedure().GetList(storedProcedureName, pageSize, currentPage, parameters);
                var gridData        = GridDataHelper.GetPaginationData(totalRecords, currentPage, pageSize);
                var dataRows        = buildDataRowsFunc(data);

                return BuildGridData(gridData, dataRows);
            }
            #endregion

            #region Private Methods
            private static jqGridData BuildGridData<T1>(PagedData<T1> gridData, IEnumerable<object[]> dataRows) where T1:Entidad
            {
                return new jqGridData(gridData.TotalRecords, gridData.TotalPages, gridData.CurrentPage, dataRows);
            }
            #endregion
        }
        #endregion

        #region Stored Procedure
        public class StoredProcedure<T> where T : Entidad
        {
            public T GetObject(string sp, Dictionary<string, object> parameters = null)
            {
                return new ControladorBase<T>.StoredProcedure().GetObject(sp, parameters);
            }
            public IList<T> GetList(string sp, Dictionary<string, object> parameters = null)
            {
                return new ControladorBase<T>.StoredProcedure().GetList(sp, parameters);
            }
        }
        #endregion
    }
}